package com.hzw.saas.common.storage.rsync;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

import cn.hutool.core.date.SystemClock;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

/**
 * Block checksum list 生成器
 *
 * @author zzl
 * @since 06/23/2021
 */
@Slf4j
public class OriginBlockBuilder {

    /**
     * 文件块长度，byte
     */
    private final int blockLength;

    /**
     * 相邻文件快步进值
     */
    private final int blockStep;

    /**
     * 文件块读取缓存
     */
    private final byte[] buffer;

    /**
     * 文件块校验和列表
     */
    @Getter
    private List<OriginBlock> blockChecksums;

    /**
     * 目标文件
     */
    private final File tarFile;

    private InputStream ins;
    private RandomAccessFile raf;

    public int getBlockLength() {
        return blockLength;
    }

    public int getBlockStep() {
        return blockStep;
    }

    public OriginBlockBuilder(File tarFile, int blockLength) {
        this(tarFile, blockLength, 0);
    }

    public OriginBlockBuilder(File tarFile, int blockLength, int blockStep) {
        Assert.notNull(tarFile, "对比文件为null");
        Assert.isTrue(blockLength > 0, "blockLength必须大于0");
        this.blockLength = blockLength;
        this.tarFile = tarFile;
        this.blockStep = blockStep > 0 ? blockStep : this.blockLength;
        this.buffer = new byte[this.blockLength];
    }

    /**
     * 生成block list
     */
    public List<OriginBlock> build(Integer checksumMode) {
        try {
            this.initParams();
            this.initFileStream();
            this.buildChecksums(checksumMode);
            return this.blockChecksums;
        } catch (Exception e) {
            throw ExceptionUtil.wrapRuntime(e);
        } finally {
            this.close();
        }
    }

    private void buildChecksums(Integer checksumMode) throws IOException {
        log.debug("build checksum start...");
        int readLength = 0;
        int blockPos = 0;
        long start = SystemClock.now();
        while (blockPos < tarFile.length()) {
            if (blockStep != this.blockLength) {
                readLength = RsyncIOUtil.readBuffer(raf, blockPos, buffer);
            } else {
                readLength = RsyncIOUtil.readBuffer(ins, buffer);
            }
            if (readLength < 0) {
                log.debug("文件读取结束...");
                break;
            }
            log.debug("读取文件快：{blockPos: {}, blockLength: {}}", blockPos, readLength);
            OriginBlock blockChecksum = new OriginBlock(blockPos, readLength);
            blockChecksum.calcChecksum(buffer, 0, checksumMode);
            blockChecksums.add(blockChecksum);
            blockPos += blockStep;
        }
        long cost = SystemClock.now() - start;
        log.debug("build checksum over, cost: {}", cost);
    }

    private void initParams() {
        this.blockChecksums = new ArrayList<>();
    }

    private void initFileStream() throws FileNotFoundException {
        ins = FileUtil.getInputStream(tarFile);
        raf = new RandomAccessFile(tarFile, "r");
    }

    private void close() {
        log.debug("build结束，关闭文件流...");
        IoUtil.close(ins);
    }

}
